package com.thlws.demojooq.strategy;

import org.jooq.codegen.DefaultGeneratorStrategy;
import org.jooq.meta.*;
import org.jooq.tools.StringUtils;

/**
 * Created by HanleyTang on 2018/11/3
 * JOOQ 自定义生成策略,将表对象放入tables 目录并对表实体添加Table后缀,与Pojo分开,以此避免同名对象书写时的不便.
 * @author Hanley[hanley@thlws.com]
 * @version 1.0
 */
public class JooqGeneratorStrategyExt extends DefaultGeneratorStrategy {


    @Override
    public String getJavaClassName(Definition definition, Mode mode) {
        String name = getFixedJavaClassName(definition);
        return name != null ? name : this.getJavaClassName0(definition, mode);
    }


    private String getJavaClassName0(Definition definition, Mode mode) {
        StringBuilder result = new StringBuilder();
        result.append(StringUtils.toCamelCase(definition.getOutputName().replace(' ', '_').replace('-', '_').replace('.', '_')));
        if (mode == Mode.RECORD) {
            result.append("Record");
        } else if (mode == Mode.DAO) {
            result.append("Dao");
        } else if (mode == Mode.POJO) {
//            result.append("Entity"); //Entity 保持习惯性命名方式,不添加后缀
        } else if (mode == Mode.INTERFACE) {
            result.insert(0, "I");
        }else if (mode == Mode.DEFAULT){
            //经验证 DEFAULT 就是数据库表,这里再进行二次检测.
            //目的是表对象后面加 Table,Pojo保持不变
            if (definition instanceof TableDefinition) {
                result.append("Table");
            }
        }

        return result.toString();
    }

    final String getFixedJavaClassName(Definition definition) {
        if (definition instanceof CatalogDefinition && ((CatalogDefinition) definition).isDefaultCatalog()) {
            return "DefaultCatalog";
        } else {
            return definition instanceof SchemaDefinition && ((SchemaDefinition) definition).isDefaultSchema() ? "DefaultSchema" : null;
        }
    }



    @Override
    public String getJavaPackageName(Definition definition, Mode mode) {
        StringBuilder sb = new StringBuilder();

        sb.append(getTargetPackage());

        // [#2032] In multi-catalog setups, the catalog name goes into the package
        if (definition.getDatabase().getCatalogs().size() > 1) {
            sb.append(".");
            sb.append(getJavaIdentifier(definition.getCatalog()).toLowerCase());
        }

        if (!(definition instanceof CatalogDefinition)) {

            // [#282] In multi-schema setups, the schema name goes into the package
            if (definition.getDatabase().getSchemata().size() > 1) {
                sb.append(".");
                sb.append(getJavaIdentifier(definition.getSchema()).toLowerCase());
            }

            if (!(definition instanceof SchemaDefinition)) {

                // Some definitions have their dedicated subpackages, e.g. "tables", "routines"
                if (!StringUtils.isBlank(getSubPackage(definition))) {
                    sb.append(".");
                    sb.append(getSubPackage(definition));
                }

                // Record are yet in another subpackage
                if (mode == Mode.RECORD) {
                    sb.append(".records");
                }

                // POJOs too
                else if (mode == Mode.POJO) {
                    sb.append(".pojos");
                }

                // DAOs too
                else if (mode == Mode.DAO) {
                    sb.append(".daos");
                }

                // Interfaces too
                else if (mode == Mode.INTERFACE) {
                    sb.append(".interfaces");
                }else{
                    if (definition instanceof TableDefinition) {
                        sb.append(".tables");
                    }
                }
            }
        }

        return sb.toString();
    }




    private String getSubPackage(Definition definition) {
        if (definition instanceof TableDefinition) {
//            return "tables";
            return "";
        }

        // [#799] UDT's are also packages
        else if (definition instanceof UDTDefinition) {
            UDTDefinition udt = (UDTDefinition) definition;

            // [#330] [#6529] A UDT inside of a package is a PL/SQL RECORD type
            if (udt.getPackage() != null)
                return "packages." + getJavaIdentifier(udt.getPackage()).toLowerCase() + ".udt";
            else
                return "udt";
        }
        else if (definition instanceof PackageDefinition) {
            return "packages";
        }
        else if (definition instanceof RoutineDefinition) {
            RoutineDefinition routine = (RoutineDefinition) definition;

            if (routine.getPackage() instanceof UDTDefinition) {
                return "udt." + getJavaIdentifier(routine.getPackage()).toLowerCase();
            }
            else if (routine.getPackage() != null) {
                return "packages." + getJavaIdentifier(routine.getPackage()).toLowerCase();
            }
            else {
                return "routines";
            }
        }
        else if (definition instanceof EnumDefinition) {
            return "enums";
        }
        else if (definition instanceof DomainDefinition) {
            return "domains";
        }

        else if (definition instanceof ArrayDefinition) {
            ArrayDefinition array = (ArrayDefinition) definition;

            // [#7125] An array inside of a package is a PL/SQL TABLE type
            if (array.getPackage() != null)
                return "packages." + getJavaIdentifier(array.getPackage()).toLowerCase() + ".udt";
            else
                return "udt";
        }

        // Default always to the main package
        return "";
    }
}
